package Data;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

public class FileData extends FileInfo implements Serializable, DataPort<BlockData> {
    // 文件对象
    public File FileObject = null;
    // 文件块大小
    public int blockSize = 1024 * 1; //1024*1024=1MB, 1024=1KB
    // 文件的总块数
    public int FileCount = 0;
    // 已经完成的块数
    public int completedBlock = 0;
    //文件内容
    public List<BlockData> FileBlock=new ArrayList<>();

    /**
     * 构造函数
     * @param path
     */
    public FileData(String path){
        this.setPath(path);
        this.FileObject = new File(path);
        this.FileCount = (int)Math.ceil((double)this.FileObject.length() / (double)this.blockSize);
        float size = (float)this.FileObject.length();
        this.setSize(size);
        this.setDataType(DataType.File);
        for (int i = 0; i < this.FileCount; i++) {
            BlockData blockData = new BlockData();
            blockData.BlockIndex = i;
            blockData.BlockSize = this.blockSize;
            blockData.BlockData = new byte[this.blockSize];
            this.FileBlock.add(blockData);
        }
        try (FileInputStream fis = new FileInputStream(this.FileObject)) {
            int totalBytesRead = 0;
            int index = 0;

            while (totalBytesRead < this.FileObject.length() && index < FileBlock.size()) {
                BlockData blockData = FileBlock.get(index);
                int bytesRead = fis.read(blockData.BlockData);

                if (bytesRead < 0) {
                    break; // 文件结束
                }

                totalBytesRead += bytesRead;

                if (bytesRead < blockData.BlockSize) {
                    // 如果读取的字节数小于块大小，调整最后一个数据块的 BlockSize
                    blockData.BlockSize = bytesRead;
                }

                index++;
            }
        } catch (FileNotFoundException e) {
            System.out.println("文件数据类抛出异常：找不到文件: " + this.FileObject.getAbsolutePath());
            e.printStackTrace();
            throw new RuntimeException(e);
        } catch (IOException e) {
            System.out.println("文件数据类抛出异常：文件读取失败: " + this.FileObject.getAbsolutePath());
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
    /**
     * 根据文件信息初始化
     * @param fileInfo
     */
    public FileData(FileInfo fileInfo) {
        super(fileInfo.getPath(), fileInfo.getSize(), fileInfo.getDataType());
        this.FileObject = new File(getPath());
        this.FileCount = (int)Math.ceil((double)this.FileObject.length() / (double)this.blockSize);
        for (int i = 0; i < this.FileCount; i++) {
            BlockData blockData = new BlockData();
            blockData.BlockIndex = i;
            blockData.BlockSize = this.blockSize;
            blockData.BlockData = new byte[this.blockSize];
            this.FileBlock.add(blockData);
        }
        try (FileInputStream fis = new FileInputStream(this.FileObject)) {
            int totalBytesRead = 0;
            int index = 0;

            while (totalBytesRead < this.FileObject.length() && index < FileBlock.size()) {
                BlockData blockData = FileBlock.get(index);
                int bytesRead = fis.read(blockData.BlockData);

                if (bytesRead < 0) {
                    break; // 文件结束
                }

                totalBytesRead += bytesRead;

                if (bytesRead < blockData.BlockSize) {
                    // 如果读取的字节数小于块大小，调整最后一个数据块的 BlockSize
                    blockData.BlockSize = bytesRead;
                }

                index++;
            }
        } catch (FileNotFoundException e) {
            System.out.println("文件数据类抛出异常：找不到文件: " + this.FileObject.getAbsolutePath());
            e.printStackTrace();
            throw new RuntimeException(e);
        } catch (IOException e) {
            System.out.println("文件数据类抛出异常：文件读取失败: " + this.FileObject.getAbsolutePath());
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 根据完整的文件数据初始化，从完整的文件数据中获取文件信息和必要的数据块
     * @param fileInfo
     * @param FileBlock
     */
    public FileData(FileData fileInfo,List<BlockData> FileBlock) {
        super(fileInfo.getPath(), fileInfo.getSize(), fileInfo.getDataType());
        this.FileBlock=FileBlock;
    }
    public FileData(){}

    /**
     * 获取当前数据块和往后数num-1的数据块
     * @param num
     * @return
     */
    public List<BlockData> getNowData(int num) {
        List<BlockData> blocks = new ArrayList<>();
        int startBlock = completedBlock;
        int endBlock = Math.min(startBlock + num, FileCount);

        for (int i = startBlock; i < endBlock; i++) {
            blocks.add(FileBlock.get(i));
        }

        return blocks;
    }

    /**
     * 当前数据块和往后数num-1的数据块发送完毕
     * @param num
     * @return 是否所有指定的数据块都已完成
     */
    public boolean NowDataIsCompleted(int num) {
        int endBlock = completedBlock + num;
        return endBlock <= FileCount;
    }
    public boolean NowDataIsCompleted() {
        return completedBlock == FileCount;
    }
    /**
     * 文件是否全部发送完毕
     * @return
     */
    public boolean FileIsCompleted() {
        return completedBlock == FileCount;
    }

    /**
     * 获取文件信息
     * @return
     */
    @Override
    public FileInfo getFileInfo() {
        FileInfo fileInfo = new FileInfo(this.getPath(), this.getSize(), this.getDataType());
        return fileInfo;
    }

    /**
     * 获取指定索引的数据
     * @param Index
     * @return
     */
    @Override
    public BlockData getIndexData(int Index) {
        return this.FileBlock.get(Index);
    }

    /**
     * 获取指定索引范围的数据
     * @param Indexs
     * @return
     */
    @Override
    public List<BlockData> getIndexsData(int[] Indexs) {
        List<BlockData> blocks = new ArrayList<>();

        for (Integer index : Indexs) {
            if (index >= 0 && index < FileCount) {
                blocks.add(FileBlock.get(index));
            } else {
                System.out.println("Index " + index + " is out of bounds.");
            }
        }
        return blocks;
    }

    /**
     * 获取所有数据
     * @return
     */
    @Override
    public List<BlockData> getAllData() {
        return this.FileBlock;
    }

    public String toString() {
        return "FileData{" +
                "文件名字(FileName)=" + this.getPath() +
                ", 每个数据块大小(blockSize)=" + blockSize +
                ", 总数据块数(FileCount)=" + FileCount +
                ", 已发送块数(completedBlock)=" + completedBlock +
                '}';
    }
}
